package com.jl.sms.userdata;

import java.security.GeneralSecurityException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;

import javax.crypto.Cipher;
import com.jl.util.HexUtil;
import com.jl.util.LengthUtil;

public class UserData {

	private String data = "";
	private int code;
	private UserDataParam param;

	public UserDataParam getParam() {
		return param;
	}

	public void setParam(UserDataParam p) {
		this.param = p;
	}

	public UserData(UserDataParam param, String data, int code) {
		this.data = data;
		this.code = code;
		this.param = param;
	}

	/**
	 * get the UserData coding scheme
	 * @return
	 */
	public int getCode() {
		return this.code;
	}

	public String getUserDataAsValue() throws GeneralSecurityException {
		if (param == null) {
			String res = new CodingScheme().codeToHex(data, code);
			return res;
		}
		String hexData = new CodingScheme().codeToHex(this.data, this.code);
		Cipher cipher = param.getCipher(Cipher.ENCRYPT_MODE);
		Cipher checksum = param.getCryptChecksum();
		if (cipher != null) {
			String forCipher = param.getCntr() + param.getPcntr() + hexData;
			int count = HexUtil.getHexCount(forCipher);
			if (checksum != null) {
				count += param.getMacLen();
			}
			// TODO:can add a padding class
			count = (8 - (count % 8)) % 8;
			for (int i = 0; i < count; i++) {
				hexData += "00 ";
			}
			param.setPcntr(HexUtil.toHexXX(count));
		} else {
			param.setPcntr("00");
		}

		if (checksum != null) {
			// sx bx spi kic kid cntr pcntr (mac) data
			int ml = param.getMacLen();
			StringBuilder sb = new StringBuilder();
			for (int i = 0; i < ml; i++) {
				sb.append('0').append('0');
			}
			String temp = sb.toString();
			String header = param.getSpi() + param.getKic() + param.getKid() + param.getTar() + param.getCntr() + param.getPcntr();
			String forMac = LengthUtil.bx(" bx " + header + temp);
			forMac = LengthUtil.sx("sx " + forMac + hexData);
			forMac = HexUtil.subHex(forMac, 0, 3) + header + hexData;
			String mac = new DesMac(forMac, checksum, param.getMacLen()).getMac();
			param.setMac(mac);
		} else {
			param.setMac("");
		}
		String finalData = "02 70 00 sx ";
		String header = "bx " + param.getSpi() + param.getKic() + param.getKid() + param.getTar() + param.getCntr() + param.getPcntr() + param.getMac();
		String headerlen = LengthUtil.bx(header).substring(0, 2);
		finalData += headerlen + param.getSpi() + param.getKic() + param.getKid() + param.getTar();

		if (cipher != null) {
			// cntr pcntr mac data
			String forCipher = param.getCntr() + param.getPcntr() + param.getMac() + hexData;
			String cryptData = HexUtil.toHexString(cipher.doFinal(HexUtil.parseHexToByteArray(forCipher)));
			finalData += cryptData;
		} else {
			finalData += param.getCntr() + param.getPcntr() + param.getMac() + hexData;
		}
		return LengthUtil.sx(finalData);
	}

	/**
	 * paser the data to UserData
	 * @param data
	 * @param code
	 * @param keyc
	 * @param keyd
	 * @param hasHeader
	 * @return
	 * @throws InvalidKeyException
	 * @throws NoSuchAlgorithmException
	 */
	public static UserData parse(String data, int code, String keyc, String keyd, boolean hasHeader) throws GeneralSecurityException {
		UserDataParam param0 = null;
		byte spi_1;
		if (hasHeader) {
			// 02 70 00 00 1F 0D 00 01 00 00 01 23 47 00 00 00 00 01 00 ---
			data = data.replaceAll("\\s+", "");
			data = data.substring(6, data.length());
			int len_ud = Integer.parseInt(data.substring(0, 4), 16);
			data = data.substring(4, data.length());
			if (len_ud != data.length() / 2) {
				return null;
			}
			param0 = new UserDataParam();
			int len_he = Integer.parseInt(data.substring(0, 2), 16);
			spi_1 = HexUtil.parseHexToByte(data.substring(2, 4));
			data = data.substring(2, data.length());
			param0.setSpi(data.substring(0, 4));
			param0.setKic(data.substring(4, 6));
			param0.setKid(data.substring(6, 8));
			param0.setTar(data.substring(8, 14));
			data = HexUtil.subHex(data, 7, HexUtil.getHexCount(data));// cntr-->end
			param0.setKeyc(keyc);
			param0.setKeyd(keyd);
			switch (spi_1 & 0x07) {
			case 0:
			case 2:
				break;
			case 4:
			case 6:
				Cipher cipher = null;
				try {
					cipher = param0.getCipher(Cipher.DECRYPT_MODE);
					data = HexUtil.toHexString(cipher.doFinal(HexUtil.parseHexToByteArray(data)));
				} catch (GeneralSecurityException e1) {
					e1.printStackTrace();
				}
				break;
			default:
				return null;
			}
			param0.setCntr(HexUtil.subHex(data, 0, 5));
			param0.setPcntr(HexUtil.subHex(data, 5, 6));
			int end = HexUtil.getHexCount(data) - (HexUtil.parseHexToByte(param0.getPcntr()) & 0xFF);
			if (len_he == 12) {
				return new UserData(param0, new CodingScheme().decodeFromHex(HexUtil.subHex(data, 6, end), code), code);//
			} else {
				param0.setMacLen(HexUtil.toHexXX(len_he - 13));
				int maclen = param0.getMacLen();
				param0.setMac(HexUtil.subHex(data, 6, 6 + maclen));
				UserData oud = new UserData(param0, new CodingScheme().decodeFromHex(HexUtil.subHex(data, 6 + maclen, end), code), code);
				return oud;
			}
		} else {
			data = new CodingScheme().decodeFromHex(data, code);
			return new UserData(null, data, code);
		}

	}

	private class DesMac {

		private byte[] array;
		Cipher cipher;
		int len = 0;

		public DesMac(String hex, Cipher cipher, int len) {
			int count = HexUtil.getHexCount(hex);
			count = (8 - (count % 8)) % 8;
			for (int i = 0; i < count; i++) {
				hex += "00 ";
			}
			array = HexUtil.parseHexToByteArray(hex);
			this.len = len;
			this.cipher = cipher;
		}

		public String getMac() throws GeneralSecurityException {
			byte[] res = cipher.doFinal(array);
			res = Arrays.copyOfRange(res, res.length - len, res.length);
			return HexUtil.toHexString(res);
		}
	}
}
